package com.zhulong.saas.cloud.gateway.security;

import com.zhulong.saas.cloud.gateway.adapt.UserAdapt;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;

import java.util.Map;

/**
 * 是否是否是当前租户对应的主体
 * <p>
 * 租户管理的地址必须校验通过才可以进行访问
 */
public class CurrentTenantReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {

    private String tenantRoleName = "tenant";

    private UserAdapt userAdapt;

    public CurrentTenantReactiveAuthorizationManager(UserAdapt userAdapt) {
        this.userAdapt = userAdapt;
    }

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
        return authentication.filter(a -> a.isAuthenticated())
                .flatMapIterable(a -> a.getAuthorities())
                .map(g -> g.getAuthority())
                .any(g -> g.contains(tenantRoleName))
                .flatMap(roleMatch -> {
                    if (!roleMatch) {
                        return Mono.just(new AuthorizationDecision(false));
                    }
                    return isCurrentTenant(authentication);
                })
                .switchIfEmpty(Mono.just(new AuthorizationDecision(false)));
    }

    private Mono<AuthorizationDecision> isCurrentTenant(Mono<Authentication> authentication) {
        return Mono.subscriberContext().filter(c -> c.get("tenantId") != null).map(c -> (Long) c.get("tenantId"))
                .flatMap(tenantId -> {
                    //获取subjectId
                    return authentication.map(a -> {
                        Map<String, Object> principal = (Map<String, Object>) a.getPrincipal();
                        Long subjectId = Long.valueOf(((Map<String, Object>) principal.get("subjectInfo")).get("id").toString());
                        boolean isOk = userAdapt.checkSubjectIsTheTenant(subjectId, tenantId);
                        return new AuthorizationDecision(isOk);
                    });
                })
                .switchIfEmpty(Mono.just(new AuthorizationDecision(false)));
    }

}
